<template>
  <div class="hdr-map">
    <h1>HDR材质贴图</h1>
    <p>
      HDR（High Dynamic Range，高动态范围）材质贴图是一种能够表示比传统LDR（Low Dynamic Range，低动态范围）图像更广泛亮度范围的纹理格式。
      它能够存储更高的亮度值，使得明亮的区域不会被截断，暗部细节也能得到保留，从而实现更真实的光照效果和材质表现。
    </p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>HDR材质贴图示例</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="canvas" class="render-conterner"></canvas>
        <div class="control-panel">
          <el-form :model="hdrMapForm" label-width="120px">
            <el-form-item label="曝光度">
              <el-slider v-model="hdrMapForm.exposure" :min="0.1" :max="5.0" :step="0.1" @change="updateExposure" />
            </el-form-item>
            <el-form-item label="色调映射">
              <el-select v-model="hdrMapForm.toneMapping" @change="updateToneMapping">
                <el-option label="线性映射" value="LINEAR" />
                <el-option label="Reinhard" value="REINHARD" />
                <el-option label="ACES电影" value="ACES" />
              </el-select>
            </el-form-item>
            <el-form-item label="金属度">
              <el-slider v-model="hdrMapForm.metalness" :min="0" :max="1" :step="0.1" @change="updateMaterialParams" />
            </el-form-item>
            <el-form-item label="粗糙度">
              <el-slider v-model="hdrMapForm.roughness" :min="0" :max="1" :step="0.1" @change="updateMaterialParams" />
            </el-form-item>
          </el-form>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>示例代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code>
  // 加载HDR贴图
  const loader = new RGBELoader()
  loader.load('/texture/brown_photostudio_02_2k.hdr', (texture) => {
    texture.mapping = THREE.EquirectangularReflectionMapping
    scene.background = texture
    material.envMap = texture
    material.needsUpdate = true

    // 设置初始渲染参数
    updateExposure()
    updateToneMapping()
    updateMaterialParams()
  })
        </code></pre>
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import * as THREE from 'three'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

// Three.js相关变量
const canvas = ref<HTMLCanvasElement | null>(null)
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: THREE.WebGLRenderer
let cube: THREE.Mesh
let controls: OrbitControls
let material: THREE.MeshStandardMaterial

// 表单数据
const hdrMapForm = reactive({
  exposure: 1.0,
  toneMapping: 'REINHARD',
  metalness: 0.7,
  roughness: 0.3
})

// 更新曝光度
const updateExposure = () => {
  if (!renderer) return
  renderer.toneMappingExposure = hdrMapForm.exposure
}

// 更新伽马值
const updateGamma = () => {
  if (!renderer) return
  renderer.outputColorSpace = THREE.LinearSRGBColorSpace
}

// 更新色调映射
const updateToneMapping = () => {
  if (!renderer) return
  renderer.toneMapping = hdrMapForm.toneMapping === 'LINEAR' ? THREE.LinearToneMapping :
                        hdrMapForm.toneMapping === 'REINHARD' ? THREE.ReinhardToneMapping :
                        THREE.ACESFilmicToneMapping
}

// 更新材质参数
const updateMaterialParams = () => {
  if (!material) return
  material.metalness = hdrMapForm.metalness
  material.roughness = hdrMapForm.roughness
  material.needsUpdate = true
}

// 初始化Three.js场景
const initThree = () => {
  if (!canvas.value) return

  // 创建场景
  scene = new THREE.Scene()

  // 创建相机
  camera = new THREE.PerspectiveCamera(
    45,
    canvas.value.clientWidth / canvas.value.clientHeight,
    0.1,
    1000
  )
  camera.position.z = 5

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({
    canvas: canvas.value,
    antialias: true
  })
  renderer.setSize(canvas.value.clientWidth, canvas.value.clientHeight)
  renderer.setPixelRatio(window.devicePixelRatio)

  // 创建控制器
  controls = new OrbitControls(camera, canvas.value)
  controls.enableDamping = true

  // 创建立方体
  const geometry = new THREE.BoxGeometry(1, 1, 1)
  material = new THREE.MeshStandardMaterial({
    roughness: 0.3,
    metalness: 0.7
  })
  cube = new THREE.Mesh(geometry, material)
  scene.add(cube)

  // 加载HDR贴图
  const loader = new RGBELoader()
  loader.load('/texture/brown_photostudio_02_2k.hdr', (texture) => {
    texture.mapping = THREE.EquirectangularReflectionMapping
    scene.background = texture
    material.envMap = texture
    material.needsUpdate = true

    // 设置初始渲染参数
    updateExposure()
    updateGamma()
    updateToneMapping()
    updateMaterialParams()
  })

  animate()
}

// 动画循环
const animate = () => {
  requestAnimationFrame(animate)

  cube.rotation.x += 0.01
  cube.rotation.y += 0.01

  controls.update()
  renderer.render(scene, camera)
}

// 处理窗口大小变化
const handleResize = () => {
  if (!canvas.value || !camera || !renderer) return

  camera.aspect = canvas.value.clientWidth / canvas.value.clientHeight
  camera.updateProjectionMatrix()
  renderer.setSize(canvas.value.clientWidth, canvas.value.clientHeight)
}

// 生命周期钩子
onMounted(() => {
  initThree()
  window.addEventListener('resize', handleResize)
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
})
</script>

<style lang="scss" scoped>
.hdr-map {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;

  h1 {
    font-size: 2em;
    color: #333;
    margin-bottom: 1rem;
  }

  p {
    color: #666;
    line-height: 1.6;
    margin-bottom: 1.5rem;
  }

  .demo-section {
    margin: 20px 0;

    .demo-content {
      display: flex;
      gap: 30px;
      align-items: flex-start;

      .render-conterner {
        width:600px;
        height: 300px;
        border: 2px solid #e6e6e6;
        border-radius: 8px;
        box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
      }

      .control-panel {
        flex: 1;
        padding: 20px;
        background-color: #f8f9fa;
        border-radius: 8px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);

        .el-form {
          margin-bottom: 20px;
        }

        p {
          font-size: 14px;
          color: #606266;
          margin: 15px 0;
        }

        ul {
          list-style-type: none;
          padding-left: 0;
          margin: 10px 0;

          li {
            margin: 8px 0;
            color: #606266;
            font-size: 14px;
            position: relative;
            padding-left: 20px;

            &::before {
              content: "•";
              position: absolute;
              left: 0;
              color: #409eff;
            }
          }
        }
      }
    }
  }

  .code-section {
    margin: 20px 0;

    .code-content {
      background-color: #f8f9fa;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);

      pre {
        margin: 0;
        white-space: pre-wrap;
        font-family: "Monaco", "Menlo", "Consolas", monospace;

        code {
          color: #409eff;
        }
      }
    }
  }
}
</style>
